漏洞接口
1 | /constDef.do?method=newConstDef |
该接口对应 com.seeyon.ctp.view.modules.operationcenter.constdef.controller.ConstDefController
的newConstDef
方法。
该方法接收了一些参数,创建了ConstDef
对象,调用了this.constDefManager.insertConstDef(def)
方法进行插入。
该方法中对constKey
、constDefine
参数进行验证,然后调用了this.findByConstKey(def.getConstKey())
该方法会以构建一个ConstDef
对象,设置constKey
,然后调用this.constDefDao.findByExample(def)
,这个方法会以constKey
查找一组ConstDef
对象,再以ConstDef
对象作为参数调用this.updateConstValue(result)
。
通过this.constDefDao.findByExample(def)
找到ConstDef
对象的情况下,会以该对象的constKey
调用ConstDefUtil.getConstDefValue(def.getConstKey())
方法。
继续以constKey
作为参数调用了ConstDefCacheManager.getInstance().getConstDefValue(constKey)
该方法首先会通过constkey
作为参数在this.cacheMap
中获取ConstDef
对象,this.cacheMap
中保存的为之前插入的ConstDef
对象。拿到ConstDef
对象后,使用ConstDef
对象作为参数调用this.computeConstValue(def)
方法。
在computeConstValue
方法中,能看到两处调用evalString
和eval
方法
通过constDefine
作为参数,使用groovy
计算表达式。
要想进入到groovy
命令执行的方法调用中,需要constDefine
的值中包含$
,且constType
需要指定为3
或者4
。
首先会调用_parserRefKey
方法对constDefine
进行解析。会通过$
界定一组关键字,如$a$
,最终会在refs
中添加a
。
调用ScriptEvaluator.getInstance().evalString(constDefine, context)
。使用"
进行包裹,进而调用了this.eval()
最后就进入到Groovy
命令执行漏洞利用点。
在ScriptEvaluator.getInstance().evalString(constDefine, context)
执行这里面会通过"
把scriptText
包裹起来,如果直接传入java.lang.Runtime.getRuntime().exec(\"calc\");
会报错,可指定constType=3
,也可通过如下方式进行拼接即可绕过。
1 | "+"java.lang.Runtime.getRuntime().exec("calc");//$a\" |
这里漏洞的利用,需要提交3次请求,第一次请求需要随机指定一个constKey
和constDefine
进行提交。
如果不指定直接提交会提示{"error":"操作异常操作失败, 常量引用不存在"}
。
所以第二次提交需要在$xx$
中引用第一次提交的constKey
,但是这一次提交不会触发命令执行,因为在调用this.constDefDao.findByExample(def)
进行查找的时候,因为constDefDao
中不存在第二次提交的constKey
,所以不会进入updateConstValue
的if条件中。只有第三次提交,才会从获取到第二次提交的ConstDef
对象,进入updateConstValue
if条件中,进而触发命令执行。
问题
ctp_const_def
表中保存的是ConstDef
对象各个属性的值,CONST_DEFINE
字段中保存的是constDefine
的值,但是该字段限制最大长度为200
。
漏洞进一步利用
由于CONST_DEFINE
保存的是执行的代码,需要先插入到数据库中,再取出执行,该字段规则限制的长度为200
。为了绕过该规则,可以通过上传一个后缀为png
,内容为webshell
的文件,然后再通过执行代码的方式将文件从上传路径移动到web路径实现RCE。
1 | java.io.File file = new java.io.File("..\\..\\base\\upload\\2023\\12\\15\\8115437553340205223");java.io.File endFile = new java.io.File("..\\webapps\\ROOT\\666.jsp");file.renameTo(endFile);//$b1$ |
1 | POST /seeyon/constDef.do HTTP/1.1 |